Repository navigation
fix(automations): remove confirmation can open with focus on body text, and Systems and Automations fixture checks fail during re-renders - #2146
Conversation
…t, and Systems and Automations fixture checks fail during re-renders The Automations remove confirmation queued its Cancel focus from Opened, which raced WinUI's own initial-focus step and sometimes left focus on the selectable body text. DialogInitialFocus redirects the first focus entering the dialog to Cancel inside WinUI's focus step, retries after layout while Cancel is not yet realized or focusable (a dialog opened right after another closed), and stops on real user input. openclaw#2124's ownership checks ran as one-shot asserts inside polling waits. While Systems and Automations re-render, a just-found element can be torn down before ownership is proven. NativeUiOwnership.Classify now returns Owned, Unproven or Foreign: polls keep polling on Unproven and still fail immediately on Foreign. IsOwnedDescendant remains Owned-only. Systems captures re-find their controls inside the wait. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🦞👀 Pull request received. I will update this pull request when review starts. ClawSweeper review completeClawSweeper finished reviewing this revision. The review result is being finalized. |
|
Codex review: needs maintainer review before merge. What this changesThe PR directs Automations confirmation focus to Cancel and makes UI ownership polling tolerate temporarily unavailable controls. Example: A user opens an automation’s Remove confirmation immediately after closing another confirmation.
Review scores
ProductKind: Bug fix · Worth it: Yes · Fix scope: Complete Merge readiness✅ Ready for maintainer review Keep open: this collaborator-authored PR addresses a documented focus defect and distinct re-render failures still present on main; no concrete introduced code defect was found. Priority: P2 Before mergeNone. FindingsNone. Tests
Agent review detailsHow this fits togetherAutomationsRoot renders native confirmation dialogs, while the UI test infrastructure observes controls and captures windows using process identity and UI Automation ancestry. flowchart TD
A[Automation action] --> B[AutomationsRoot confirmation]
B --> C[DialogInitialFocus]
C --> D[Native Cancel button]
E[Fixture UI tests] --> F[NativeUiOwnership]
F --> G[Owned window capture]
Technical reviewBest possible solution: Retain the focused dialog repair and tri-state observation changes while preserving explicit Cancel focus and strict final ownership checks. Do we have a high-confidence way to reproduce the issue? The original queued focus implementation and one-shot ownership assertions match the reported failure paths, and the author records a deterministic focus negative control; this read-only review ran no target code. Is this the best way to solve the issue? The patch preserves the existing owners and invariants: replace queued-only initial focus, distinguish unavailable observations from foreign controls, and reacquire controls inside capture waits; the helper’s native lifecycle boundary justifies its separate name and state. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning medium; reviewed against d1a5b4609690. Provenance checked
TestingProof path: in-process harness. SecurityNone. EvidenceWhat I checked:
Preflight sourceSource unknown. Source comparison at the observed PR snapshot only. Completion is self-reported; body, policy, tests, execution, and report bytes are not verified. Review metrics
LabelsLabel changes:
Label justifications:
Rating scale6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior. WorkflowClawSweeper edits this one comment on every review. Comment Reviewed November 13, 2026, 12:04 AM ET / 05:04 UTC. |
…ened fires CI showed ContentDialog.Opened firing before the Cancel template part is realized even on the first open. Every confirmation now uses the timing-independent check: the first focus inside the dialog once Cancel exists is Cancel, Cancel keeps it, and no other element takes focus afterwards. The "later focus is never stolen" proof focuses the body after Cancel holds focus and requires it to stay. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Related: #2124, #2132
What Problem This Solves
Fixes: the Automations remove confirmation can open with keyboard focus on its body text instead of Cancel, and the Systems and Automations mock Gateway UI tests fail intermittently on
mainwhile those pages re-render.User Impact
User impact: when you remove an automation, the confirmation now always opens with keyboard focus on Cancel, the safe choice it was designed to focus, instead of sometimes on the body text. Contributors also get fewer false CI failures in the "Mock Gateway application and compatibility tests" job.
Why This Change Was Made
Automations remove confirmation (product fix). Before this change,
AutomationsRootqueued a low-priority "focus Cancel" call from the dialog'sOpenedevent. WinUI runs its own initial-focus step, which picks the first focusable element (the selectable body text). Whichever ran last won, so focus sometimes stayed on the body. CI caught this as "Timed out waiting for Cancel receives destructive initial focus" (run 43323497743, attempt 2).The new
DialogInitialFocushelper redirects the first focus that enters the dialog to Cancel inside WinUI's own focus step, the same approachSystemsPageViewalready uses. When a dialog opens right after another closes,Openedand even the first focus can arrive before the Cancel button exists. The helper then retries after each layout pass until Cancel takes focus. Later focus moves are never redirected.Systems and Automations UI tests (test fix). #2124 made these tests check that every UI element they find belongs to the app's own window. Some of those checks ran inside polling waits as one-shot asserts. While a page re-renders, an element found a moment earlier can be torn down before the ownership check finishes, so the check could not prove ownership and the test failed. It should have looked again.
Ownership now has three results:
IsOwnedDescendantstill returns true only for Owned, so the ownership guarantee from #2124 is unchanged. The Systems capture helper also re-finds its controls inside its wait, instead of reusing elements that a re-render may have replaced.The changes add no retries of actions, sleeps, fixed settle counts, skips, or weaker assertions.
Evidence
Openedas before, the newDialogInitialFocusTestsfails with "confirmation-0: first focus after Cancel was realized went to TextBox". With the fix it passes.Openedcan precede the dialog's controls: diagnostics showedOpenedfiring with zero visual children before the focus step; on the CI runner this happens even on the first open. Before the retry-until-realized change, the body kept focus in about 1 of 25 quick reopens. The test therefore never samples focus atOpened. It records every focus change through the dialog's ownGettingFocusand opens 11 confirmations per run, each right after the previous one closed. For each one it requires:It also requires that a later focus move to the body is not taken back.
NativeUiOwnershipClassificationTestschecks Owned, Unproven and Foreign deterministically over synthetic ancestry, including:The existing live two-window test now also asserts Foreign for every rejection case.
ElementNotAvailableExceptionwhile saving a capture of re-rendered stat tiles. The same pattern was reproduced locally inAutomationsTryPageScrollPeer("page marker must belong", Dark theme).Change Type
Scope
winnodeRequired proof pools
none: the product change is limited to keyboard focus in the Automations confirmation dialog. It is proven by native in-process and mock Gateway UI tests that CI runs directly; no custom capacity-dependent Windows proof applies.Validation
All runs were local on Windows x64 at 175% DPI, with isolated app and test profiles. Metadata for the real
%APPDATA%/%LOCALAPPDATA%\OpenClawTraywas unchanged after every run. Fixture runs usedOPENCLAW_RUN_KEYBOARD_PROOF=1and screenshots, matching CI.Final source (hashes recorded before the commit; this commit is that source rebased onto
origin/mainee8edbc with no conflicts):DialogInitialFocusTests+NativeUiOwnershipTests+NativeUiOwnershipClassificationTests(8 tests; the dialog test opens 11 confirmations per run)Automations_ScopesToAgentsAndCoversMainStates(Light + Dark)SystemsDaily_InsetAndExpandedNativeFramesOpenedFull fixture UI suite (filter
Category=GatewayFixture&KeyboardProof!=Required, run directly because the script's 1800 s cap is shorter than the ~31 min local run at 175% DPI), on the final source together with the separate pointer/capture PR's test change: 122/124 passed. The two failures are in tests this PR does not change:Automations_HeroWashScrollsWhileTheBackplateStaysAnchored(Dark): "Timed out waiting for theme choice". The theme ComboBox did not open after a one-shot UIAExpand()in the unchangedSelectAppThemeAsync. A rerun passed 3/3.ReconnectKeepsDefinitelyUnsentQueuedMessageFailedAndVisible: MCPapp.chat.send"Request canceled". This test is unchanged frommain, and it also fails on main's own test code locally (2 of 3 reruns). Reported separately as a pre-existing issue.After rebasing onto
origin/mainee8edbc (#2141):.\scripts\validate-test-timing-patterns.ps1: "Test timing pattern ratchet passed." No baseline-lowering suggestion.Closeout on the rebased commit:
./build.ps1: all builds succeeded.dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj --no-restore: 5849 passed, 0 failed, 29 skipped.dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --no-restore: 6402 passed, 0 failed.Real Behavior Proof
dotnet test tests/OpenClaw.Tray.UITests -c Debug -r win-x64 -p:DevBuild=false --no-build --filter <test>, with the fixture environment fromscripts/test-gateway-fixture.ps1(OPENCLAW_RUN_GATEWAY_FIXTURE_UI=1,OPENCLAW_RUN_KEYBOARD_PROOF=1, screenshots on).DialogInitialFocusTests, plus theAutomations_ScopesToAgentsAndCoversMainStatesremove step, which waits for UI Automation to report keyboard focus on the dialog's Cancel button.Yes/No/N/A): N/A (local artifacts only).Opened, and on the runnerOpenedfired before Cancel existed. 7f120bc fixes the test; the product code is unchanged. The Mock Gateway job, which includes SystemsDaily and the Automations remove step, passed on that run. CI for 7f120bc is pending.AssertUnchangedstill runs after the Systems capture wait.Pendingwhile a dialog without a focusable Cancel stays open.Security Impact
Yes/No): NoYes/No): NoYes/No): NoYes/No): NoYes/No): NoYes, explain the risk and mitigation: N/ACompatibility and Migration
Yes/No): YesYes/No): NoYes/No): NoReview Conversations